import json
import random
import uuid
from time import sleep

import requests
from flask import Flask
from flask_sqlalchemy import SQLAlchemy # 为啥标红的？？
from sqlalchemy.orm import class_mapper

from src.main.python.generate.log import set_loguru_logger

logger = set_loguru_logger()

app = Flask(__name__)

# AttributeError: 'NoneType' object has no attribute 'encoding'
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:123456@localhost:3306/web" # ?charset=utf-8
app.config["SQLALCHEMY_ECHO"] = True
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
# 自动保存，不用commit 不管用！
# app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"] = True

db = SQLAlchemy(app)

# 模型类
class Soft(db.Model):
    __tablename__ = "tb_soft"
    id = db.Column(db.Integer, primary_key=True)
    soft_name = db.Column(db.String(100))
    soft_version = db.Column(db.String(100))
    git_url = db.Column(db.String(100))
    official_url = db.Column(db.String(100))
    main_language = db.Column(db.String(100))
    languages = db.Column(db.String(100))
    star = db.Column(db.Integer, default=0)
    uuid = db.Column(db.String(100))
    soft_id = db.Column(db.String(100))

    def __repr__(self):
        return "<Soft(soft_name='%s', soft_version='%s, git_url='%s', languages='%s')>" % (
            self.soft_name, self.soft_version, self.git_url, self.languages
        )

def get_header():
    user_agent = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36",
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
        'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1",
        "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
        "Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; Tablet PC 2.0; .NET4.0E)",
        "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)",
        "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
        "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)",
        "Mozilla/5.0 (Windows; U; Windows NT 6.1; ) AppleWebKit/534.12 (KHTML, like Gecko) Maxthon/3.0 Safari/534.12",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)",
        "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.33 Safari/534.3 SE 2.X MetaSr 1.0",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 QQBrowser/6.9.11079.201",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E) QQBrowser/6.9.11079.201",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
    ]
    headers = {
        'Accept-Encoding': 'gzip, deflate, sdch',
        'Accept-Language': 'en-US,en;q=0.8',
        'User-Agent': random.choice(user_agent),
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Referer': 'http://www.wikipedia.org/',
        'Connection': 'close', # close keep-alive
        'Authenticated': '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'
    }

    return headers

def request_url(url):
    sleep(3)
    logger.info(url)

    code = ""
    try:
        res = requests.get(url=url, headers=get_header())
        code = res.status_code
        if code == 200:
            return {
                "code": code,
                "msg": "success",
                "data": res.json()
                # "data": json.loads(res.text)
            }
    except Exception as e:
        logger.ERROR(f"e: {e}")
    return {
        "code": code,
        "msg": "request_url ERROR!",
        "data": ""
    }

def get_github_soft(query_name):
    url = f"https://api.github.com/search/repositories?q={query_name}&page=1&per_page=50&sort&order"
    ans = request_url(url=url)
    logger.debug(ans)
    return ans

def get_latest_version(repo_name):
    url = f"https://api.github.com/repos/{repo_name}/releases/latest"
    ans = request_url(url=url)
    logger.debug(ans)
    return ans

@app.route("/")
def arrive_home():
    return "Hello World!"

@app.route("/querySoft", methods=["GET"])
def query_soft():
    try:
        all = Soft.query.all()
        results = []
        for result in all:
            mapper = class_mapper(result.__class__)
            results.append({column.key: getattr(result, column.key) for column in mapper.columns})
        logger.info(results)
        return json.dumps({
            "code": 200,
            "msg": "success",
            "data": {
                "list": results
            },
            "page": {
                "total": len(results),
                "pageNum": "",
                "pageSize": ""
            }
        }, indent=4, ensure_ascii=False)
    except Exception as e:
        logger.error(e)
    return json.dumps({
        "code": 500,
        "msg": "query_soft ERROR!",
        "data": ""
    }, indent=4, ensure_ascii=False)

@app.route("/deleteSoft", methods=["GET"])
def delete_soft():
    try:
        # index = Soft.query.filter_by(soft_id="11730342").delete()
        index = Soft.query.delete() # 全删
        db.session.commit() # 不加commit 不管用！
        """
        2023-11-04 at 01:57:13 | ERROR | 
        Textual SQL expression 'ALTER TABLE web.tb_soft A...' 
        should be explicitly declared as text('ALTER TABLE web.tb_soft A...')
        """
        # db.session.execute("ALTER TABLE web.tb_soft AUTO_INCREMENT=1;")
        # db.session.execute("ALTER TABLE web.tb_soft AUTO_INCREMENT=1")
        return json.dumps({
            "code": 200,
            "msg": "success",
            "data": index
        }, indent=4, ensure_ascii=False)
    except Exception as e:
        logger.error(e)

    return json.dumps({
        "code": 500,
        "msg": "delete_soft ERROR!",
        "data": ""
    }, indent=4, ensure_ascii=False)


def add_soft(full_name):
    ans = get_github_soft(query_name=full_name)
    code = 500
    if ans["code"] == 200:
        items = ans["data"].get("items")[0:1] # 只拿搜到的前1个
        logger.debug(f"items: {items}") # 空的？
        added_soft = []
        for item in items:
            latest_version = get_latest_version(item["full_name"])
            data = latest_version["data"]
            soft_version = "" if isinstance(data, str) else data.get("html_url")
            soft = Soft(
                soft_name=item["full_name"],
                soft_version=soft_version,
                git_url=item["git_url"],
                official_url=item["homepage"],
                main_language=item["language"],
                languages=item["language"],
                star=item["stargazers_count"],
                soft_id=item["id"],
                uuid=uuid.uuid1()
            )
            # db.session.add(soft)
            added_soft.append(soft)
        db.session.add_all(added_soft)
        # 若只是修改数据，不存在则新增，可以使用merge()方法
        db.session.commit()  # 提交会话(事务) # 放这儿只有一条数据 # 怎么避免重复数据？
        return {
            "code": 200,
            "msg": "success",
            "data": items,
            "total": len(items)
        }
    return {
        "code": code,
        "msg": "add_soft ERROR!",
        "data": ""
    }

@app.route("/addSoft")
def maintenance_soft():
    maintenance_names = [
        "vue", "react", "angular", "Bun", # 前端
        "flask", "Tornado", "django", "Sanic", # python
        "gin", "gorm", # go
        "spring boot", "mybatis", "mybatis-plus", "mybatis-flex" # java
    ]
    code = 500
    try:
        for name in maintenance_names:
            ans = add_soft(name)
            code = ans["code"]
            logger.info(ans)
        return json.dumps({
            "code": code,
            "msg": "success",
            "data": maintenance_names,
            "total": len(maintenance_names)
        }, indent=4, ensure_ascii=False)
    except Exception as e:
        logger.error(f"maintenance_soft: {e}")

    return json.dumps({
        "code": code,
        "msg": "maintenance_soft ERROR!",
        "data": ""
    }, indent=4, ensure_ascii=False)


if __name__ == '__main__':
    with app.app_context():
        app.run(debug=True)

